package com.example.demojimmer.application.services;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import com.example.demojimmer.application.dto.Results;
import com.example.demojimmer.config.JwtConfig;
import com.example.demojimmer.config.SecurityConfig;
import com.example.demojimmer.domain.userauth.model.entity.dto.LoginUserInput;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

@RequiredArgsConstructor
@Validated
@Service
public class LoginApplicationService {
    private final AuthenticationManager authenticationManager;

    //长token过期时间 秒
    public static final long EXPIRYL = 60L * 60 * 1;
    //短token过期时间 秒
    public static final long EXPIRYS = 60L * 1;

    public Results<List<TokenVo>> doLogin(LoginUserInput loginUserInput) {

        //登录未认证前使用 UsernamePasswordAuthenticationToken.unauthenticated 构建
        UsernamePasswordAuthenticationToken unauthenticateded = UsernamePasswordAuthenticationToken.unauthenticated(
                loginUserInput.getEmail(),//可以自定义 使用的唯一性字段  例如邮箱
                loginUserInput.getPassword() //密码
        );

        //使用authenticationManager.authenticate验证 UsernamePasswordAuthenticationToken
        Authentication authentication = authenticationManager.authenticate(unauthenticateded);
        //验证成功放入SecurityContex
        SecurityContextHolder.getContext().setAuthentication(authentication);
        SecurityConfig.User userDetails = (SecurityConfig.User) authentication.getPrincipal();

        List<TokenVo> tokenRes = getTokenVos(userDetails);

        /*jwtToken = Jwt.builder()
                .setSubject(userDetails.getUsername()) // 主题，通常是用户名
                .setIssuedAt(new Date()) // 签发时间
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时后过期
                .signWith(SignatureAlgorithm.RS256, "your-secret-key".getBytes()).compact();*/

        return Results.res(0, "成功", tokenRes);
    }

    private @NotNull List<TokenVo> getTokenVos(SecurityConfig.User userDetails) {
        TokenVo tokenS = createToken(userDetails, EXPIRYS, "TokenS");
        TokenVo tokenL = createToken(userDetails, EXPIRYL, "TokenL");

        List<TokenVo> tokenRes = new ArrayList<>();
        tokenRes.add(tokenS);
        tokenRes.add(tokenL);
        return tokenRes;
    }

    @Getter
    @Setter
    public static class TokenVo {
        private String name;
        private String jwtToken;
        //生效时间
        private String effectiveTime;
        //失效时间
        private String expirationTime;
    }

    public Results<List<TokenVo>> refreshToken() {
        //获取认证过的用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        SecurityConfig.User userDetails = (SecurityConfig.User) authentication.getPrincipal();

        List<TokenVo> tokenRes = getTokenVos(userDetails);
        return Results.ok(tokenRes);
    }

    public TokenVo createToken(SecurityConfig.User userDetails, Long expiresAt, String tokenName) {
        String jwtToken = "";
        DateTimeFormatter norm_datetime_pattern = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN).withZone(ZoneId.of("Asia/Shanghai"));//DateTimeFormatter 处理Instant需要指明ZoneId

        System.out.println(JSONUtil.toJsonStr(userDetails));
        //生效日期
        Instant now = Instant.now();
        String effectiveTime = norm_datetime_pattern.format(now);
        
        //角色列表字符串，必须用空格分隔
        //String scope = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(","));

        //短token失效时间
        Instant expiresAtInstant = now.plusSeconds(expiresAt);

        //非对称私钥 生成
       /* KeyPair rs256 = KeyUtil.generateKeyPair(SignAlgorithm.SHA256withRSA.getValue());
        PrivateKey aPrivate = rs256.getPrivate();
        PublicKey aPublic = rs256.getPublic();
        System.out.println(KeyUtil.toBase64(aPrivate));
        System.out.println(KeyUtil.toBase64(aPublic));*/

        RSA rsa = new RSA(JwtConfig.privateKey, JwtConfig.publicKey);
        //私 加签
        JWTSigner jwtSigner = JWTSignerUtil.rs256(rsa.getPrivateKey());
        JWT jwt = JWT.create()
                //JWT头信息
                /* 这两签名时会自动放入  解析时会自动识别
                .setHeader(JWTHeader.TYPE, "JWT")
                .setHeader(JWTHeader.ALGORITHM, "RS256")*/

                //Claim
                //生效时间 ，未到时间不被认可
                //.setPayload(JWTPayload.NOT_BEFORE,effectiveTime)
                //过期时间  不设置就是永久token
                //.setPayload(JWTPayload.EXPIRES_AT, norm_datetime_pattern.format(expiresAtInstant))//
                .setPayload(JWTPayload.EXPIRES_AT, DateUtil.date(expiresAtInstant))//
                .setPayload(JWTPayload.ISSUED_AT, now)//签发时间
                .setPayload(JWTPayload.SUBJECT, JSONUtil.toJsonStr(userDetails))
                //自定义 tokenName
                .setPayload("tokenName", tokenName)
                //签名算法
                .setSigner(jwtSigner);

        //生成token
        jwtToken = jwt.sign();

        TokenVo token = new TokenVo();
        token.setName((String) jwt.getPayload("tokenName"));
        token.setJwtToken(jwtToken);
        //有效期
        token.setEffectiveTime(effectiveTime);
        token.setExpirationTime(norm_datetime_pattern.format(expiresAtInstant));

        return token;
    }

    //秘钥生成
    public static void main(String[] args) {
        KeyPair rs256 = KeyUtil.generateKeyPair(SignAlgorithm.SHA256withRSA.getValue());
        PrivateKey aPrivate = rs256.getPrivate();
        PublicKey aPublic = rs256.getPublic();
        System.out.println(KeyUtil.toBase64(aPrivate));
        System.out.println(KeyUtil.toBase64(aPublic));
    }
}
